package cn.edu.gzmtu.controller;

import cn.edu.gzmtu.domain.SysLog;
import cn.edu.gzmtu.service.ISysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * Aop日志操作
 * 1.前置通知
 * （1）获取开始时间
 * （2）执行的类的名称
 * （3）执行的方法名称
 * <p>
 * <p>
 * 2.后置通知
 * （1）获取访问时长：获取当前时间，减去开始时间
 * （2）获取访问的路径
 * （3）获取访问的ip地址
 * （4）将日志相关的信息封装到SysLog对象
 */
@Component
@Aspect
public class LogAop {

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private ISysLogService sysLogService;

    private Date visitTime; // 开始时间
    private Class clazz;    // 访问的类
    private Method method;  // 访问的方法

    /**
     * 前置通知
     *
     * @param jp
     */
    @Before("execution(* cn.edu.gzmtu.controller.*.*(..))")
    public void doBefore(JoinPoint jp) throws NoSuchMethodException {
        visitTime = new Date(); // 当前的时间就是开始访问的时间
        clazz = jp.getTarget().getClass(); // 获取具体要访问的类

        String methodName = jp.getSignature().getName(); // 获取要方法的方法的名称
        Object[] args = jp.getArgs();// 获取执行方法的参数
        if (args == null || args.length == 0) {
            method = clazz.getMethod(methodName); // 只能获取到无参的方法
        } else {
            Class[] classArgs = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classArgs[i] = args[i].getClass();
            }
            method = clazz.getMethod(methodName, classArgs); // 获取有参的方法
        }

    }

    /**
     * 后置通知
     *
     * @param jp
     */
    @After("execution(* cn.edu.gzmtu.controller.*.*(..))")
    public void doAfter(JoinPoint jp) throws Exception {

        ///////////获取访问时长///////////////////////////////////////////////////
        long time = new Date().getTime() - visitTime.getTime(); // 获取访问时长

        ///////////获取访问的路径///////////////////////////////////////////////////
        String url = "";
        // 获取url="类上的路径(RequestMapping注解的值)" + "方法上的路径(RequestMapping注解的值)"
        if (clazz != null && method != null && clazz != LogAop.class) {

            // 1.获取类上注解@RequestMapping("xxx")的值
            RequestMapping clazzAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class); // 获取类指定的注解
            if (clazzAnnotation != null) {
                String classValue = clazzAnnotation.value()[0];

                // 2.获取方法上注解@RequestMapping("xxx")的值
                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                if (methodAnnotation != null) {
                    String methodValue = methodAnnotation.value()[0];

                    url = classValue + methodValue;
                }


                ////////////获取访问的ip地址//////////////////////////////////////////////////
                // 通过request获取访问的ip地址
                // 需要在web.xml配置RequestContextListener监听，使用@Autowired注解来属性注入
                String ip = request.getRemoteAddr();


                ////////////获取当前操作的用户//////////////////////////////////////////////////
                // (1) 通过Spring Security来获取
                SecurityContext context = SecurityContextHolder.getContext(); // 从上下文中获取当前登录的用户
                User user = (User) context.getAuthentication().getPrincipal();
                String username = user.getUsername();

                // (2) 通过Session来获取
                // String name = (String) request.getSession().getAttribute("SPRING_SECURITY_CONTEST");

                ////////////将日志信息存储到数据库//////////////////////////////////////////////////
                // (1)将日志相关的信息封装到SysLog对象
                SysLog sysLog = new SysLog();
                sysLog.setExecutionTime(time);
                sysLog.setIp(ip);
                sysLog.setMethod("[类名] " + clazz.getName() + "[方法名]" + method.getName());
                sysLog.setUsername(username);
                sysLog.setVisitTime(visitTime);
                sysLog.setUrl(url);

                // (2) 调用service完成添加日志操作
                sysLogService.save(sysLog);

            }
        }
    }
}
